分隔線(Divider)元件在 UI/UX 設計中扮是個重要的輔助角色,用於在視覺上區分不同的內容區域,增強版面結構的清晰度和可讀性。
在實際網頁開發中,大多數時候我們會選用 CSS Class 來達到相關的畫面需求,例如在 TailwindCSS / UnoCSS 中,我們可以透過 divide-x
、divide-y
這類的 Utils Class 來達到 Divider 的效果。
<div class="grid grid-cols-3 divide-x">
<div>01</div>
<div>02</div>
<div>03</div>
</div>
但是在一些情況下,我們可能會需要更多的客製化功能,並且需要明確的無障礙可讀性來幫助使用者更明確地分辨出不同的區塊。這時候我們就需要 Divider 這個元件。
在開始實作前,我們先研究各個 UI Library 的 Divider 元件是如何設計的。
Element Plus
<template>
<div>
<span>What you are you do not see, what you see is your shadow.</span>
<ElDivider content-position="left">Rabindranath Tagore</ElDivider>
<span>
My wishes are fools, they shout across thy song, my Master. Let me but
listen.
</span>
<ElDivider>
<ElIcon><StarFilled /></ElIcon>
</ElDivider>
<span>I cannot choose the best. The best chooses me.</span>
<ElDivider content-position="right">Rabindranath Tagore</ElDivider>
</div>
</template>
除了一般分隔線,Element Plus 提供了 content-position
來為 default slot 的內容定位,預設為 center
,也可以依照需求設定為 left
或 right
。
Element Plus 也支援使用 direction="vertical"
來設定垂直分隔線。
不過在垂直分隔線的情況下,default slot 的內容並不會被渲染。
Nuxt UI
<template>
<div class="space-y-4">
<!-- 略 -->
</div>
<UDivider orientation="vertical">OR</UDivider>
<div class="space-y-4">
<!-- 略 -->
</div>
</template>
在 Nuxt UI,我們可以透過 orientation
來設定 Divider 的方向,預設為 horizontal
,也可以設定為 vertical
。
Nuxt UI 不論水平或垂直的 Divider 都支援顯示 default slot,但不支援靠左、靠右或是靠上、靠下的定位方式。
綜合以上並結合自身經驗,我們統整出 <AtomicDivider>
的功能:
orientation
設定 Divider 的方向。text-align
設定 default slot 的內容定位。定位的部分,在 Element Plus 使用 left
、center
、right
這樣的方式,但因為希望 orientation
為 vertical
時也可以支援,所以可以接受的名稱調整為 start
、center
、end
。
首先,我們將需求中提到的功能整理成 props
的介面,我們會需要下列屬性:
名稱 | 型別 | 預設值 | 說明 |
---|---|---|---|
orientation | horizontal , vertical |
horizontal |
Divider 的方向 |
text-align | start , center , end |
center |
default slot 的內容定位 |
interface AtomicDividerProps {
orientation: 'horizontal' | 'vertical'
textAlign: 'start' | 'center' | 'end'
}
const props = withDefaults(defineProps<AtomicDividerProps>(), {
orientation: 'horizontal',
textAlign: 'center'
})
首先我們來規劃模板的部分:
<template>
<div
v-if="$slots.default"
class="atomic-divider"
:class="[
`atomic-divider--${props.orientation}`,
`atomic-divider--${props.textAlign}`
]"
>
<span class="atomic-divider__wrapper">
<slot name="default" />
</span>
</div>
<hr
v-else
class="atomic-divider"
:class="[
`atomic-divider--${props.orientation}`
]"
>
</template>
如果使用者有使用 default slot,我們就渲染一個 <div>
元素,分隔線的部分我們使用 CSS 繪製;如果使用者單純使用分隔線的話,我們就選擇 <hr>
元素渲染。
接著我們來處理 SCSS 的內容,我們可以使用偽元素 ::before
與 ::after
來繪製分隔線,它會長這樣子。
div.atomic-divider {
display: flex;
justify-content: center;
align-items: center;
&::before,
&::after {
content: '';
display: block;
}
&--horizontal {
&::before,
&::after {
width: var(--atomic-divider-size, 50%);
border-top: 1px solid lightslategray;
}
}
&--vertical {
flex-direction: column;
&::before,
&::after {
height: var(--atomic-divider-size, 50%);
border-left: 1px solid lightslategray;
}
}
}
現在我們已經涵蓋了水平與垂直置中對齊的情況,再來只要處理 text-align
為 start
、end
這兩種情況。在上面我們預留了 CSS 的變數,所以我們只要在對應的 CSS Class 中設定變數的值。
.atomic-divider {
&--end {
&::before {
--atomic-divider-size: 90%;
}
&::after {
--atomic-divider-size: 10%;
}
}
&--start {
&::before {
--atomic-divider-size: 10%;
}
&::after {
--atomic-divider-size: 90%;
}
}
}
最後我們來處理沒有 default slot 的情況,也就是渲染為 <hr>
元素的情況。
hr.atomic-divider {
border: none;
&--horizontal {
border-top: 1px solid lightslategray;
width: 100%;
}
&--vertical {
border-left: 1px solid lightslategray;
height: auto;
}
}
這樣就完成了我們的 <AtomicDivider>
元件了!
在 HTML 中 <hr>
標籤有分隔器(separator)的語意在裡面,但當我們使用 <div>
復刻出一樣的視覺效果時,也別忘了為使用螢幕閱讀器的使用者提供相同的語意。
<template>
<div
class="atomic-divider"
role="separator"
>
<!-- 略 -->
</div>
</template>
但如果使用 <AtomicDivider>
只是為了美化 UI,那我們可以使用 role="presentation"
這個設定。使用 role="presentation"
意味著這個元素僅作為視覺裝飾,對無障礙技術來說是無意義的,不會被輔助技術解釋為分隔線。
<template>
<AtomicDivider role="presentation" />
</template>
在實現部分,通過使用偽元素 ::before
和 ::after
,我們能夠簡單有效地繪製分隔線,並且搭配 CSS 變數讓我們不需要過多的設定,就能達到理想的效果。
Divider 在我們的網頁開發中幾乎無所不在,如果遇到只是用於美化 UI 的需求,能用 CSS 解決其實就很足夠了!但如果遇到需要更細部樣式調整,或是像 Nuxt UI 的範例那樣,需要明確分隔兩個區塊,這時候就非常推薦使用 <AtomicDivider>
這個元件,它同時能達到需求,也能確保使用螢幕閱讀器的使用者能正確地理解網頁的內容。
<AtomicDivider>
原始碼:AtomicDivider.vue